Skip to main content

Login redirects and subscription paywalls

Supacharger has three related settings, but they do different jobs:

SettingWhat it controlsWhat it does not control
USER_REDIRECTS.AUTHED_USER.LOGIN_REDIRECT_DESTINATIONThe normal destination after password sign-in and after a successful magic-link or social OAuth callbackGeneral “home” links and access control
USER_REDIRECTS.AUTHED_USER.HOME_PATHThe authenticated home used by general navigation and flows such as completed password resetThe PKCE callback destination
BILLING_ACCESS.REQUIREDWhether the magic-link/social callback checks billing access and diverts a user without accessA site-wide route guard or database authorisation policy

Protected server routes use three access levels:

BoundaryChecksAppropriate routes
requireVerifiedUser()Verified, non-anonymous identity according to configurationProfile setup and other identity recovery
requireOnboardedUser()Verified identity, then configured profile completionSubscription acquisition
requireAppAccess()Verified identity, onboarding first, then configured billing accessFull product routes

A recovery destination must not inherit the guard for the condition it recovers. Route groups can give /account/setup-profile a verified-only layout and /account/billing/subscribe an onboarded layout without changing either public URL.

Redirect order in the current core

For a magic link or social provider, Supabase first returns the browser to the allow-listed application callback, normally /auth/callback. Supacharger then processes the result in this order:

  1. Exchange the PKCE code for a session and verify that a user exists.
  2. If POST_SIGN_IN_ONBOARDING.REQUIRED is enabled and the profile is incomplete, redirect to POST_SIGN_IN_ONBOARDING.REDIRECT_PATH.
  3. If BILLING_ACCESS.REQUIRED is enabled, call the server-side billing-access function. A user without access is redirected to BILLING_ACCESS.REDIRECT_PATH.
  4. Otherwise, redirect to LOGIN_REDIRECT_DESTINATION and add the one-time successful-login notice.

In compact form:

Supabase callback
-> valid session?
-> required profile complete?
-> required billing access present?
-> LOGIN_REDIRECT_DESTINATION

Password sign-in navigates directly to LOGIN_REDIRECT_DESTINATION; it does not run callback policy in /auth/callback. The matched protected server layout then enforces the appropriate verified, onboarded, or full-app boundary before rendering. Proxy remains claims-only and performs neither database check.

BILLING_ACCESS.REQUIRED: true protects the canonical authenticated layout and callback journey, but it is not a substitute for handler-specific authorisation. Enforce entitlement checks in protected APIs and Server Actions that do not render through that layout, and keep Supabase Row Level Security as the final database boundary where appropriate.

One authenticated landing page, no forced subscription

Use this when users may enter the product without paying:

USER_REDIRECTS: {
AUTHED_USER: {
HOME_PATH: '/app',
AUTHGUARD_REDIRECT_DESTINATION: '/app',
LOGIN_REDIRECT_DESTINATION: '/app',
},
},
BILLING_ACCESS: {
REQUIRED: false,
FEATURE_LOOKUP_KEY: null,
REDIRECT_PATH: '/account/billing/subscribe?full=1',
},

This is the simplest default. All normal authenticated navigation and successful sign-ins converge on /app. Paid features should check their own entitlement when used.

Public marketing home and authenticated product home

If / is a public marketing page, do not use / as the authenticated destination unless returning signed-in users to marketing is deliberate:

USER_REDIRECTS: {
UNAUTHED_USER: {
HOME_PATH: '/',
AUTHGUARD_REDIRECT_DESTINATION: '/account/login',
LOGOUT_REDIRECT_DESTINATON: '/',
},
AUTHED_USER: {
HOME_PATH: '/dashboard',
AUTHGUARD_REDIRECT_DESTINATION: '/dashboard',
LOGIN_REDIRECT_DESTINATION: '/dashboard',
},
},

Keeping the three authenticated destinations aligned avoids surprising differences between login, “home”, and attempts to revisit the login page.

LOGOUT_REDIRECT_DESTINATON must be an application-relative path beginning with one /, never an absolute or protocol-relative URL. The managed logout button receives this path as JSON from POST /account/logout after the SSR session is cleared. A developer-owned direct link may use GET /account/logout, which clears the same session and responds with 303 plus that relative path in Location. The browser resolves both forms against the origin it opened rather than NEXT_PUBLIC_SITE_URL.

Callback subscription detour

Use this only when passwordless/social callbacks should send users without the required access to the subscription page:

BILLING_ACCESS: {
REQUIRED: true,
FEATURE_LOOKUP_KEY: 'product_access',
REDIRECT_PATH: '/account/billing/subscribe?full=1',
},

FEATURE_LOOKUP_KEY should be a stable entitlement lookup key. A value of null uses the compatibility rule of any trialing or active Subscription. The redirect path must exist beneath an onboarded-only boundary and must remain reachable by a profile-complete authenticated user who does not yet have access. Placing it beneath requireAppAccess() creates a self-redirect loop.

If the whole product must be paywalled, also add server-side entitlement enforcement to protected page layouts, route handlers, Server Actions, and APIs. Apply the same policy to password sign-in so every authentication method behaves consistently. A successful Stripe Checkout return is not itself proof of access; use the local entitlement projection populated by webhooks and reconciliation.

Different post-login and home destinations

The two paths may deliberately differ:

AUTHED_USER: {
HOME_PATH: '/app',
AUTHGUARD_REDIRECT_DESTINATION: '/app',
LOGIN_REDIRECT_DESTINATION: '/welcome',
},

Use this only for an unconditional post-login landing page. For conditional profile setup, use POST_SIGN_IN_ONBOARDING instead; it runs before the billing check and normal callback destination. Ensure /welcome does not redirect an authenticated user back to login or create a loop.

Supabase URL Configuration is a separate layer

These Supacharger values are application paths after a session has been established. They do not replace the hosted Supabase settings under Authentication → URL Configuration.

  • Set the Supabase Site URL to the production origin.
  • Add the exact production callback URL, such as https://example.com/auth/callback, to Redirect URLs.
  • Add the password-reset and confirmation destinations used by the project.
  • Add localhost and preview URLs only for the environments that need them; prefer exact production paths over broad wildcards.
  • Keep NEXT_PUBLIC_SITE_URL aligned with the deployed origin so Supacharger builds the same callback origin that Supabase allows.

Supabase validates the full redirectTo URL before returning a passwordless or social authentication flow. After the browser reaches /auth/callback, Supacharger applies the onboarding, billing, and login-destination decisions described above. See the official Supabase Redirect URLs guide.

Release checklist

  • Every configured path starts with / and exists in the application.
  • The onboarding destination inherits only requireVerifiedUser() and the subscription destination inherits only requireOnboardedUser().
  • No recovery response repeats the same effective URL after one recovery hop.
  • All enabled authentication methods reach the intended product destination.
  • Password, magic-link, OTP, and social login are tested separately because they do not all use /auth/callback.
  • Paid pages and APIs reject missing entitlements on the server, independently of browser redirects.
  • Supabase RLS protects paid or private database data where client access is possible.
  • The hosted Supabase callback allow-list and NEXT_PUBLIC_SITE_URL match production.
Build Supacharger with SpecdriveKeep the specification, infrastructure and agent work in one project context.